#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 13

char command_line[LINE_SIZE]; //接收命令行参数
extern char** environ;
char* argv[ARGC_SIZE]; //保存命令行参数
char pwd[LINE_SIZE];   //保存当前路径
char myenv[LINE_SIZE]; //保存导入的环境变量，这里只有一个
int quit = 0;
int lastcode = 0;

const char* getpwd()
{
    return getcwd(pwd, sizeof(pwd));
}

const char* getUser()
{
    return getenv("USER");
}

const char* getHostname()
{
    return getenv("HOSTNAME");
}

const char* getPwd()
{
    return getenv("PWD");
}

void interact(char* cline, size_t size)
{
    printf("%s@%s:%s$ ",getUser(),getHostname(),getPwd());
    char* s = fgets(cline, size, stdin);
    
    assert(s);
    
    // ls -a -l\n\0
    cline[strlen(cline) - 1] = '\0';
}

int splitstring(char cline[], char *_argv[])
{
    int i = 0;
    _argv[i++] = strtok(cline, DELIM); // 先将第一个部分进行切割
    while(_argv[i++] = strtok(NULL, DELIM)); // NULL表示不会返回起始地址
    return i - 1; 
}

void normalExcu(char* _argv[])
{
   pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return;
    }
    else if(id == 0) // child
    {
        execvp(_argv[0], _argv);
        exit(EXIT_CODE); // 替换失败
    }
    else // father
    {
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);//等待子进程
        if(ret == id)
        {
            lastcode = WEXITSTATUS(status);
        }
    }
}

int builitInCommand(char* _argv[], int _argc)
{
    if(_argc == 2 && strcmp(_argv[0], "cd") == 0)
    {
        chdir(_argv[1]); //修改路径
        getpwd();
        sprintf(getenv("PWD"), "%s",pwd); //将新的路径写入环境变量PWD中
        return 1; //表示内建命令执行
    }
    else if(_argc == 2 && strcmp(_argv[0], "export") == 0)
    {
        strcpy(myenv, _argv[1]); //将导入的环境变量保存在全局数据区，避免被覆盖
        putenv(myenv);  //将全局数据区的环境变量导入到系统的环境变量表中
        return 1;
    }
    else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)
    {
        if(strcmp(_argv[1], "$?") == 0) //获取上一个指令的退出码
        {
            printf("%d\n", lastcode);
            lastcode=0;
        }
        else if(*_argv[1] == '$'){ //获取环境变量
            char *val = getenv(_argv[1]+1);
            if(val) printf("%s\n", val);
        }
        else{
            printf("%s\n", _argv[1]); //直接打印，这里没有对""做处理
        }

        return 1;
    }

    // 特殊处理一下ls
    if(strcmp(_argv[0], "ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
    return 0;  //表示需要执行普通命令
}

int main()
{

    while(!quit)
    {
        //交互逻辑，解析命令行输入
        interact(command_line, sizeof(command_line));

        //指令切分
        int argc = splitstring(command_line, argv);
        if(argc == 0) 
            continue;

        // for dubug
        // for(int i = 0; argv[i]; i++) 
        //     printf("[%d] -> %s\n",i , argv[i]);

        //区分指令类型
        
        //内建命令
        int n = builitInCommand(argv, argc);
        //普通命令
        if(!n) normalExcu(argv);
    }

    return 0;
}